/**
 * Created on 2019年11月28日 by hexin
 */
package com.ahdms.es.util;

import com.ahdms.es.gm.asn1.cipher.SM2Signature;
import org.bouncycastle.asn1.ASN1Integer;
import org.bouncycastle.asn1.ASN1Sequence;
import org.bouncycastle.asn1.x500.style.X500NameTokenizer;
import org.bouncycastle.asn1.x509.Certificate;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigInteger;
import java.util.Date;

/**
 * @author hexin
 * @version 1.0
 * @Title
 * @Description
 * @Copyright <p>
 * Copyright (c) 2019
 * </p>
 * @Company <p>
 * 迪曼森信息科技有限公司 Co., Ltd.
 * </p>
 * @修改记录
 * @修改序号，修改日期，修改人，修改内容
 */
public class CertUtil {

    //颁发机构公钥矩阵资源路径
    public static String pkmPath;

    private static String ocspUrl;

    public static String getOcspUrl() {
        return ocspUrl;
    }

    public void setOcspUrl(String ocspUrl) {
        CertUtil.ocspUrl = ocspUrl;
    }

    public void setPkmPath(String pkmPath) {
        CertUtil.pkmPath = pkmPath;
    }

    static Logger logger = LoggerFactory.getLogger(CertUtil.class);

    // 证书验证返回码定义
    public final static int CERT_VALIDATE_SUCCESS = 1;// 证书有效
    public final static int CERT_VALIDATE_NOT_TRUST = -1;// 证书不被信任
    public final static int CERT_VALIDATE_HAS_EXPIRED = -2;// 超过有效期范围
    public final static int CERT_VALIDATE_NOT_CANCEL = -3;// 证书已作废
    public final static int CERT_VALIDATE_NOT_FREEZE = -4;// 证书已冻结
    public final static int CERT_VALIDATE_INEFFECTIVE = -5;// 证书未生效
    public final static int CERT_VALIDATE_UNKNOWN_ERROR = -6;// 其他错误
    public final static int CERT_VALIDATE_NOT_DigitalSignature = -7;// 非签名密钥用法


    //证书验证级别定义
    public static final int VERIFYLEVEL_0 = 0;  //验证时间
    public static final int VERIFYLEVEL_1 = 1;  //验证时间和根证书签名
    public static final int VERIFYLEVEL_2 = 2;  //验证时间，根证书签名和OISP

    //颁发机构公钥矩阵缓存
    public static Certificate pkm = null;

    /**
     * 验证证书
     * @param cert
     * @return
     */
    public static int validateCert(Certificate cert) {
        //验证证书有效期
        int result = validateCertTime(cert);
        if (result == CERT_VALIDATE_SUCCESS) {
            //验证根证书签名
            boolean validateCertSignResult = validateCertSign(cert, getPKM());
            if (!validateCertSignResult) {
                return CERT_VALIDATE_NOT_TRUST;
            }
            //验证密钥用法
            boolean b = certTypeJudge(cert, "1");
            if (!b) {
                return CERT_VALIDATE_NOT_DigitalSignature;
            }
            //验证证书是否被吊销
//            Integer verifyOISPResult = OCSPUtils.verifyCert(cert, getPKM(), ocspUrl);
//            if (verifyOISPResult == 0) {
//                //验证OISP通过
//            } else if (verifyOISPResult == 1) {
//                //证书被撤销
//                return CERT_VALIDATE_NOT_CANCEL;
//            } else {
//                //验证OISP未知错误
//                return CERT_VALIDATE_UNKNOWN_ERROR;
//            }
        }
        return result;
    }

    /**
     * 判断证书类型
     *
     * @param cert 证书
     * @param type 证书类型 0:加密；1:签名
     * @return
     * @创建人 hexin
     * @创建时间 2017年10月20日
     * @创建目的【】
     * @修改目的【修改人：，修改时间：】
     */
    private static boolean certTypeJudge(Certificate cert, String type) {
        boolean result = false;

        KeyUsage keyUsage = null;
        try {
            Extension keyUsageExt = cert.getTBSCertificate().getExtensions().getExtension(Extension.keyUsage);
            keyUsage = KeyUsage.getInstance(ASN1Sequence.fromByteArray(keyUsageExt.getExtnValue().getOctets()));
            if ("1".equals(type)) {
                if (keyUsage.hasUsages(KeyUsage.digitalSignature)) {
                    result = true;
                }
            }
            if ("0".equals(type)) {
                if (keyUsage.hasUsages(KeyUsage.dataEncipherment)) {
                    result = true;
                }
            }
        } catch (Exception ex) {

        }
        return result;
    }

//    public static void main(String[] args) throws IOException {
//        Certificate cert = Certificate.getInstance(FileUtils.fileToByte("E:/mine/bjcaqm.cer"));
//        Extension extensions = cert.getTBSCertificate().getExtensions().getExtension(Extension.keyUsage);
//        KeyUsage keyUsage = KeyUsage.getInstance(ASN1Sequence.fromByteArray(extensions.getExtnValue().getOctets()));
//        if(keyUsage.hasUsages(KeyUsage.digitalSignature)) {
//            System.out.println("ssss");
//        }
//    }


    /**
     * 验证证书有效期
     *
     * @param cert
     * @return
     * @创建人 hexin
     * @创建时间 2018年9月26日
     * @创建目的【】
     * @修改目的【修改人：，修改时间：】
     */
    private static int validateCertTime(Certificate cert) {

        Date date = new Date();
        if (date.getTime() > cert.getEndDate().getDate().getTime()) {
            return CERT_VALIDATE_HAS_EXPIRED;
        }

        if (date.getTime() < cert.getStartDate().getDate().getTime()) {
            return CERT_VALIDATE_INEFFECTIVE;
        }

        return CERT_VALIDATE_SUCCESS;
    }

    /**
     * 获取颁发机构公钥矩阵
     *
     * @return
     * @创建人 hexin
     * @创建时间 2019年11月27日
     * @创建目的【】
     * @修改目的【修改人：，修改时间：】
     */
    private static Certificate getPKM() {
        Certificate certificate = null;//颁发机构公钥矩阵

        if (null != pkm) {
            //从缓存中获取公钥矩阵
            certificate = pkm;
        } else {
            //从本地包中的资源路径下加载公钥矩阵，并放入缓存中
            InputStream inputStream = null;
            try {
                inputStream = new FileInputStream(pkmPath);
                byte[] pkmBytes = new byte[inputStream.available()];
                inputStream.read(pkmBytes);
                ASN1Sequence as = AsnUtil.byteToASN1Sequence(pkmBytes);
                pkm = Certificate.getInstance(as);

                certificate = pkm;
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (null != inputStream) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

        logger.debug("颁发机构公钥矩阵subject：" + certificate.getTBSCertificate().getSubject().toString());

        return certificate;
    }

    /**
     * 验证证书标识私钥签名（硬件验证）
     *
     * @param cert 证书
     * @param pkm  公钥矩阵
     * @return
     * @创建人 hexin
     * @创建时间 2018年5月31日
     * @创建目的【】
     * @修改目的【修改人：，修改时间：】
     */
    private static boolean validateCertSign(Certificate cert, Certificate pkm) {
        boolean verifyResult = false;

        try {
            //证书标识
            String identity = getPartFromDN(cert.getSubject().toString(), "CN");
            logger.debug("证书标识：" + identity);

            //通过标识和公钥矩阵计算标识公钥（软实现）
            byte[] publicKey = IKIUtil.getPublicKeyFromPKM(pkm, identity);
            logger.debug("根证书签名公钥长度:" + publicKey.length);

            //根证书签名原文
            byte[] signData = cert.getTBSCertificate().getEncoded();
            logger.debug("根证书签名原文长度:" + signData.length);

            //根证书签名值
            byte[] sign = cert.getSignature().getBytes();
            logger.debug("根证书签名值长度:" + sign.length);

            //调用软件接口验证签名
            verifyResult = SM2Utils.verifySign(publicKey, signData, AsnUtil.asn1ToByte(getSM2Signature(sign)));
            logger.debug("验证根标识签名返回结果:" + verifyResult);

        } catch (Exception e) {
            logger.error("证书验证标识私钥签名异常：", e);
        }

        return verifyResult;
    }

    /**
     * 组装SM2Signature数据,定义见GM/T0009,编码格式r||s
     *
     * @param sign 签名值
     * @return
     * @创建人 hexin
     * @创建时间 2016年11月1日
     * @创建目的【】
     * @修改目的【修改人：，修改时间：】
     */
    private static SM2Signature getSM2Signature(byte[] sign) {
        SM2Signature sm2Signature = null;
        if (null != sign) {
            //byte[] bsR = Util.subBytes(sign, 0, 32); //取签名byte数组中R部分
            //byte[] bsS = Util.subBytes(sign, 64,32); //取签名byte数组中S部分

            byte[] bsR = ByteUtils.subBytes(sign, 0, 32); //取签名byte数组中R部分
            byte[] bsS = ByteUtils.subBytes(sign, 32, 32); //取签名byte数组中S部分
			
/*			BigInteger bigR = new BigInteger(bsR);
			BigInteger bigS = new BigInteger(bsS);

			logger.debug("签名值R部分:"+bigR.toString());
			logger.debug("签名值S部分:"+bigS.toString());*/

            ASN1Integer R = new ASN1Integer(new BigInteger(1, bsR));
            ASN1Integer S = new ASN1Integer(new BigInteger(1, bsS));
            sm2Signature = new SM2Signature(R, S);
        }
        return sm2Signature;
    }


    private static String getPartFromDN(String dn, String dnpart) {
        String part = null;
        if ((dn != null) && (dnpart != null)) {
            String o;
            dnpart += "="; // we search for 'CN=' etc.
            X500NameTokenizer xt = new X500NameTokenizer(dn);
            while (xt.hasMoreTokens()) {
                o = xt.nextToken();
                // log.debug("checking: "+o.substring(0,dnpart.length()));
                if ((o.length() > dnpart.length()) && o.substring(0, dnpart.length()).equalsIgnoreCase(dnpart)) {
                    part = o.substring(dnpart.length());

                    break;
                }
            }
        }
        return part;
    }

    public static String validateCertMessage(int code) {
        String message = "证书验证失败";
        switch (code) {
            case CERT_VALIDATE_NOT_TRUST:
                message = "证书不被信任";
                break;
            case CERT_VALIDATE_HAS_EXPIRED:
                message = "证书超过有效期范围";
                break;
            case CERT_VALIDATE_NOT_CANCEL:
                message = "证书已作废";
                break;
            case CERT_VALIDATE_NOT_FREEZE:
                message = "证书已冻结";
                break;
            case CERT_VALIDATE_INEFFECTIVE:
                message = "证书未生效";
                break;
            case CERT_VALIDATE_NOT_DigitalSignature:
                message = "非签名密钥用法";
                break;
            default:
                break;
        }
        return message;
    }

}
